#ifndef MEASURE_SIZE_TRACKER_CURSOR_VISITOR_H_
#define MEASURE_SIZE_TRACKER_CURSOR_VISITOR_H_

#if defined(_MSC_VER)
#pragma once
#endif

#include "geometry_elements.h"
#include "boost\variant\static_visitor.hpp"

class cursor_visitor : public boost::static_visitor<int>
{
public:

  cursor_visitor(int hit_code, const point_2& point)
    : hit_code_(hit_code)
    , point_(point)
  {
  }

  ~cursor_visitor(void)
  {
  }

public:

  int operator()(const point_2& point) const
  {
    BOOST_ASSERT(point_2::kHitMiddle==hit_code_);
    return kCursorHand;
  }

  int operator()(const segment_2& segment) const
  {
    BOOST_ASSERT(segment_2::kHitOn==hit_code_ || \
      segment_2::kHitStart==hit_code_ || segment_2::kHitEnd==hit_code_);

    if (segment_2::kHitOn==hit_code_) {
      return kCursorSizeAll;
    } else {
      return kCursorCross;
    }
  }

  int operator()(const box_2& box) const
  {
    BOOST_ASSERT(box_2::kHitOn<=hit_code_ &&\
      hit_code_<=box_2::kHitSouthEast);

    switch (hit_code_)
    {
    case box_2::kHitEast:
      return kCursorSizeWE;

    case box_2::kHitNorthEast:
      return kCursorSizeNESW;

    case box_2::kHitNorth:
      return kCursorSizeNS;

    case box_2::kHitNorthWest:
      return kCursorSizeNWSE;

    case box_2::kHitWest:
      return kCursorSizeWE;

    case box_2::kHitSouthWest:
      return kCursorSizeNESW;

    case box_2::kHitSouth:
      return kCursorSizeNS;

    case box_2::kHitSouthEast:
      return kCursorSizeNWSE;

    case box_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }
  }

  int operator()(const angle_box_2& angle_box) const
  {
    BOOST_ASSERT(angle_box_2::kHitOn<=hit_code_ &&\
      hit_code_<=angle_box_2::kHitRight);

    switch(hit_code_)
    {
    case angle_box_2::kHitEast:
      return draw_obj::kBorderCursors[draw_obj::quadrant(angle_box.angle)];

    case angle_box_2::kHitNorthEast:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+1)%8];

    case angle_box_2::kHitNorth:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+2)%8];

    case angle_box_2::kHitNorthWest:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+3)%8];

    case angle_box_2::kHitWest:
      return draw_obj::kBorderCursors[draw_obj::quadrant(angle_box.angle)];

    case angle_box_2::kHitSouthWest:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+1)%8];

    case angle_box_2::kHitSouth:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+2)%8];

    case angle_box_2::kHitSouthEast:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+3)%8];

    case angle_box_2::kHitTop:
    case angle_box_2::kHitBottom:
      return draw_obj::kBorderCursors[draw_obj::quadrant(angle_box.angle)];

    case angle_box_2::kHitLeft:
    case angle_box_2::kHitRight:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(angle_box.angle)+2)%8];

    case angle_box_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }
  }

  int operator()(const ring_2& ring) const
  {
    BOOST_ASSERT(ring_2::kHitOn<=hit_code_ && hit_code_<=ring_2::kHitHandle);

    switch(hit_code_)
    {
    case ring_2::kHitBorder:
    case ring_2::kHitHandle:
      return draw_obj::kBorderCursors[draw_obj::quadrant(point_2(ring.center_x,\
        ring.center_y), point_)];

    case ring_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }
  }

  int operator()(const arc_2& arc) const
  {
    BOOST_ASSERT(arc_2::kHitOn<=hit_code_ && hit_code_<=arc_2::kHitEnd);

    switch(hit_code_)
    {
    case arc_2::kHitBorder:
      return draw_obj::kBorderCursors[draw_obj::quadrant(point_2(arc.center_x,\
        arc.center_y), point_)];

    case arc_2::kHitStart:
    case arc_2::kHitEnd:
      return kCursorCross;

    case arc_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }
  }

  int operator()(const annulus_2& annulus) const
  {
    BOOST_ASSERT(annulus_2::kHitOn<=hit_code_ && hit_code_<=annulus_2::kHitHandle);

    switch(hit_code_)
    {
    case annulus_2::kHitStartBorder:
    case annulus_2::kHitEndBorder:
    case annulus_2::kHitHandle:
      return draw_obj::kBorderCursors[draw_obj::quadrant(point_2(annulus.center_x,\
        annulus.center_y), point_)];

    case annulus_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }
  }

  int operator()(const annulus_arc_2& annulus_arc) const
  {
    BOOST_ASSERT(annulus_arc_2::kHitOn<=hit_code_ &&\
      hit_code_<=annulus_arc_2::kHitEndEnd);

    switch(hit_code_)
    {
    case annulus_arc_2::kHitStartBorder:
    case annulus_arc_2::kHitEndBorder:
      return draw_obj::kBorderCursors[draw_obj::quadrant(point_2(annulus_arc.center_x,\
        annulus_arc.center_y), point_)];

    case annulus_arc_2::kHitStartSpoke:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(annulus_arc.start_angle)+2)%8];

    case annulus_arc_2::kHitEndSpoke:
      return draw_obj::kBorderCursors[(draw_obj::quadrant(annulus_arc.end_angle)+2)%8];

    case annulus_arc_2::kHitStartStart:
    case annulus_arc_2::kHitEndStart:
    case annulus_arc_2::kHitStartEnd:
    case annulus_arc_2::kHitEndEnd:
      return kCursorCross;

    case annulus_arc_2::kHitOn:
      return kCursorSizeAll;

    default:
      return kCursorArrow;
    }

    return kCursorArrow;
  }

  int operator()(const polygon_2& polygon) const
  {
    BOOST_ASSERT(polygon_2::kHitOn<=hit_code_ && hit_code_<polygon.num_points()*2);

    if (hit_code_>=0) {
      if (hit_code_&0x1) {
        point_2 sp = polygon.at(hit_code_/2);
        point_2 ep = polygon.at(((hit_code_+1)/2)%polygon.num_points());
        
        return draw_obj::kBorderCursors[(draw_obj::quadrant(sp, ep)+2)%8];
      } else {
        return kCursorCross;
      }
    } else {
      return kCursorArrow;
    }
  }

private:
  int hit_code_;
  const point_2& point_;
};

#endif // MEASURE_SIZE_TRACKER_CURSOR_VISITOR_H_